module mmu(
  input         clk,
  input         rst,
  input         i_virt_addr_valid,
  input  [31:2] i_virt_addr,
  output        o_phys_addr_valid,
  output [31:2] o_phys_addr,
  //
  input         i_wb_ack,
  input  [31:0] i_wb_dat,
  output        o_wb_stb,
  output [31:2] o_wb_adr
);

reg o_phys_addr_valid;
reg o_phys_addr;
reg o_wb_stb;
reg o_wb_adr;

reg [31:0] r1; // Control
reg [31:0] r2; // Translation Table Base

reg [ 1:0] state;
reg [31:2] virt_addr_r;

localparam S_INIT = 0;
localparam S_L1_FETCH = 1;
localparam S_L2_FETCH = 2;

always @(posedge clk) begin
  if (rst) begin
    r1 <= 0;
    r2 <= 0;
  end else begin
    if (r1[0]) begin
      case (1'b1)
        state[S_INIT]: begin
          virt_addr_r <= i_virt_addr;
          // address of first-level descriptor
          o_wb_stb <= 1'b1;
          o_wb_adr <= {r2[31:14],virt_addr_r[31:20]};
          state <= 1<<S_L1_FETCH;
        end
        state[S_L1_FETCH]: begin
          if (i_wb_ack) begin
            state <= 1<<S_L2_FETCH;
            o_wb_adr <= {i_wb_dat[31:10],virt_addr_r[19:12]};
            state <= S_L2_FETCH;
          end
        end
        state[S_L2_FETCH]: begin
          if (i_wb_ack) begin
            o_wb_stb <= 1'b0;
            o_phys_addr_valid <= 1'b1;
            o_phys_addr <= {i_wb_dat[31:12],virt_addr_r[11:2]};
            state <= S_INIT;
          end
        end
      endcase
    end else begin
      o_phys_addr_valid <= i_virt_addr_valid;
      o_phys_addr <= i_virt_addr;
    end
  end
end

endmodule
